﻿#!/usr/bin/env python
# -*- coding: utf-8 -*-

# bios.py

# Copyright 2009 Diego Hansen Hahn (aka DiegoHH) <diegohh90 [at] hotmail [dot] com>

# lazynds is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.

# lazynds is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with lazynds. If not, see <http://www.gnu.org/licenses/>.

''' Wrapper para as rotinas de compressão da BIOS '''

__author__ = "Diego Hansen Hahn"
__version__ = "v2.0.4"

import heapq
import os
import struct

import gtk

from compression import (lz40, lzss, onz, rle, huffman)
import pluginmanager
import resources
import timedebugger

class bios(list):
    '''
    Corresponde a BIOS do GBA/NDS, mas seu uso pode ser expandido através
    de plugins.

    NOTA: A compressão LZSS 0x11 não é definida na BIOS, mas foi colocada aqui
    por praticidade - Hansen
    '''

    MIN_SIZE = 0x20
    MAX_SIZE = 0xFFFF
    MAX_SEARCH = False # Não estou certo se mantenho ou não esse parâmetro - Hansen

    FLAGS = {0x10: "LZSS 0x10",
             0x11: "LZSS 0x11",
             0x24: "Huffman 4-Bits",
             0x28: "Huffman 8-Bits",
             0x30: "RLE",
             0x40: "LZSS 0x40"}

    def __init__(self, rc = None):
        # Adiciona métodos de lista à BIOS
        # Será usado para manipular com maior facilidade os resultados - Hansen
        list.__init__(self)

        #setattr(self, 'fmap', fmap)
        setattr(self, 'compressions', list())

        if rc:
            resources.parse_xml_resource(rc, self)

    def plugin_scan(self, fmap, plugin):
        results = pluginmanager.trigger_event(plugin + '-SCAN',
                                              infile = fmap,
                                              min_size = self.MIN_SIZE,
                                              max_size = self.MAX_SIZE,
                                              max_search = self.MAX_SEARCH)

        while gtk.events_pending():
            gtk.main_iteration()

        for data in results[0]:
            heapq.heappush(self, data)

    def bios_scan(self, fmap):
        i = 0

        while True:
            try:
                while ord(fmap[i]) not in self.compressions:
                    i += 4

                fmap.seek(i, 0)

                header = struct.unpack('<L', fmap.read(4))[0]
                size = header >> 8
                flag = header & 0xFF

                if ((size % (32 if self.MAX_SEARCH else 1)) == 0
                and size <= self.MAX_SIZE
                and size >= self.MIN_SIZE):
                    if flag == 0x10 and lzss.try_uncompress(fmap, i, size):
                        self.append((i, 0x10))
                    elif flag == 0x11 and onz.try_uncompress(fmap, i, size):
                        self.append((i, 0x11))
                    elif flag == 0x30 and rle.try_uncompress(fmap, i, size):
                        self.append((i, 0x30))
                    elif flag == 0x24 and huffman.try_uncompress(fmap, i, size):
                        self.append((i, 0x24))
                    elif flag == 0x28 and huffman.try_uncompress(fmap, i, size):
                        self.append((i, 0x28))
                    elif flag == 0x40 and lz40.try_uncompress(fmap, i, size):
                        self.append((i, 0x40))

                while gtk.events_pending():
                    gtk.main_iteration()

                i += 4
            except:
                break

    def uncompress(self, infile, index):
        flag = self.get_flag(index)
        address = self.get_address(index)

        try:
            if flag == 0x10:
                return lzss.uncompress(infile, address)
            elif flag == 0x11:
                return onz.uncompress(infile, address)
            elif flag == 0x30:
                return rle.uncompress(infile, address)
            elif flag in (0x24, 0x28):
                return huffman.uncompress(infile, address)
            elif flag == 0x40:
                return lz40.uncompress(infile, address)
            else:
                return pluginmanager.trigger_event(('%s-UNCOMPRESS' % flag),
                                                     infile = infile,
                                                     address = address)[0]
        except:
            return False

    def compress(self, infile, flag):
        if flag == 0x10:
            return lzss.compress(infile)
        elif flag == 0x11:
            return onz.compress(infile)
        elif flag == 0x30:
            return rle.compress(infile)
        elif flag in (0x24, 0x28):
            return huffman.compress(infile, (type & 0xf))
        elif flag == 0x40:
            return lz40.compress(infile)
        else:
            return pluginmanager.trigger_event(('%s-COMPRESS' % flag), infile = infile)[0]

    def get_flag(self, index):
        return self[index][1]

    def get_address(self, index):
        return self[index][0]

    def size(self):
        return len(self)

    def save(self, rc):
        resources.create_xml_resource(rc)
        resources.update_xml_resource(rc, self)
        return
